#include "UdpServer.hpp"
#include "OnlineClient.hpp"
#include <unordered_map>
#include <fstream>
#include <memory>
#include <cstdio>
#include <signal.h>

std::unordered_map<std::string, std::string> dict;//词典
const std::string mydict = "./dict.txt";//词典的位置

void Usage(std::string proc)//使用手册
{
    // std::cout << "\n[Usage]\n" << "\t" << proc << "\tlocalip\tlocalport\n" << std::endl;
    std::cout << "\n[Usage]\n\t" << proc << "\tlocalport\n" << std::endl;
    exit(USAGE_ERR);
}

bool cut_string(const std::string &line, std::string* key, std::string* value, const std::string &sep)//切割字符串
{
    size_t pos = line.find(sep);
    if(pos == std::string::npos) return false;//说明没有该分隔符
    *key = line.substr(0, pos);//[)
    *value = line.substr(pos + sep.size());//[)
    return true;
}

void dict_init()//初始化词典
{
    std::ifstream fin(mydict, std::ios::in);
    if(!fin.is_open())
    {
        std::cerr << errno << ": "<< strerror(errno) << std::endl;
        exit(OPEN_ERR);
    }
    std::string line;
    std::string key, value;
    while(getline(fin, line))
    {
        // std::cout << line << std::endl;
        if(cut_string(line, &key, &value, ":"))
        {
            dict.insert(std::make_pair(key, value));
        }
    }
    fin.close();
    std::cout << "dict init success!" << std::endl;
}

void debug_dict_init()//用于测试词典是否初始化成功
{
    for(auto &it: dict)
    {
        std::cout << it.first << "# " << it.second << std::endl;
    }
}

void update_dict(int signum)//用于热加载词典：无需将服务器关闭就能够对词典进行更新
{
    dict_init();
    std::cout << "reload dict success" << std::endl;
}

void translate(int socketfd, std::string clientip, uint16_t clientport, std::string message)//服务器对客户端发来的单词进行翻译
{
    auto it = dict.find(message);
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    client.sin_port = htons(clientport);
    std::string response;
    if(it == dict.end()) response = "unknown";
    else response = it->second;
    int n = sendto(socketfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
    if(n == -1)
    {
        std::cerr << errno << ": "<< strerror(errno) << std::endl;
        exit(SENDTO_ERR);
    }    
}

void command_exec(int socketfd, std::string clientip, uint16_t clientport, std::string command)
{
    char buffer[buffer_num];
    std::string response;
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = inet_addr(clientip.c_str());
    client.sin_port = htons(clientport);
    FILE* command_ret = popen(command.c_str(), "r");
    if(command_ret == nullptr) response = "open err";
    else
    {
        while(fgets(buffer, sizeof(buffer)-1, command_ret))
        {
            response += buffer;
        }
    }
    int n = sendto(socketfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
    if(n == -1)
    {
        std::cerr << errno << ": "<< strerror(errno) << std::endl;
        exit(SENDTO_ERR);
    }
    pclose(command_ret);
}

OnlineClient onlineClient;

void route_message(int socketfd, std::string clientip, uint16_t clientport, std::string message)
{
    std::string response;
    if(message == "online") { onlineClient.add_user(socketfd, clientip, clientport); return;}
    if(!onlineClient.is_online(socketfd, clientip, clientport))
    {
        // std::cout << "please online at first! you should enter \"online\" to make it!" << std::endl;
        return;
    }
    if(message == "offline") { onlineClient.del_user(socketfd, clientip, clientport); return;}
    onlineClient.broadcast_message(socketfd, clientip, clientport, message);
}

int main(int argc, char* argv[])
{
    // signal(2, update_dict);

    if(argc < 2)
    {
        Usage(argv[0]);
    }

    // dict_init();
    // debug_dict_init();

    uint16_t port = atoi(argv[1]);
    // std::unique_ptr<UdpServer> us(new UdpServer(translate, port));
    // std::unique_ptr<UdpServer> us(new UdpServer(command_exec, port));
    std::unique_ptr<UdpServer> us(new UdpServer(route_message, port));
    us->init();
    us->start();
    return 0;
}